package cn.cdu.basics.security.jwt;

import cn.cdu.basics.utils.SecurityUtil;
import cn.cdu.basics.baseVo.TokenUser;
import cn.cdu.basics.parameter.LoginProperties;
import cn.cdu.data.utils.NullUtils;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
public class JwtRoleFilter extends BasicAuthenticationFilter {

    private final SecurityUtil securityUtil;
    private final StringRedisTemplate stringRedisTemplate;
    private final LoginProperties loginProperties;

    public JwtRoleFilter(AuthenticationManager manager, LoginProperties loginProperties,
                         StringRedisTemplate redis, SecurityUtil securityUtil) {
        super(manager);
        this.stringRedisTemplate = redis;
        this.securityUtil = securityUtil;
        this.loginProperties = loginProperties;
    }

    public JwtRoleFilter(AuthenticationManager manager, AuthenticationEntryPoint point) {
        super(manager, point);
        this.stringRedisTemplate = null;
        this.securityUtil = null;
        this.loginProperties = null;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        String uri = request.getRequestURI();
        if (uri.startsWith("/swagger")
                || uri.startsWith("/v2/api-docs")
                || uri.startsWith("/v3/api-docs")
                || uri.startsWith("/swagger-ui")
                || uri.startsWith("/swagger-resources")
                || uri.startsWith("/webjars")) {
            log.info("JWT过滤器放行Swagger请求: {}", uri);
            chain.doFilter(request, response);
            return;
        }

        String tokenHeader = request.getHeader(LoginProperties.HTTP_HEADER);
        if (NullUtils.isNull(tokenHeader)) {
            tokenHeader = request.getParameter(LoginProperties.HTTP_HEADER);
        }
        log.info("Received token header: {}", tokenHeader);

        if (NullUtils.isNull(tokenHeader)) {
            log.info("No token header found, skipping authentication");
            chain.doFilter(request, response);
            return;
        }

        try {
            UsernamePasswordAuthenticationToken token = getUsernamePasswordAuthenticationToken(tokenHeader);
            if (token != null) {
                SecurityContextHolder.getContext().setAuthentication(token);
                log.info("Authentication successful for user: {}", token.getPrincipal());
            }
        } catch (Exception e) {
            log.error("Authentication failed: {}", e.getMessage(), e);
        }

        chain.doFilter(request, response);
    }

    /**
     * 判断登录是否失效 (不直接写 response)
     */
    private UsernamePasswordAuthenticationToken getUsernamePasswordAuthenticationToken(String header) {
        log.info("[JWT] 开始校验 token, header(accessToken): {}", header);
        String redisKey = LoginProperties.HTTP_TOKEN_PRE + header;
        String tokenInRedis = stringRedisTemplate.opsForValue().get(redisKey);
        log.info("[JWT] Redis 查找 key: {}, 查到的值: {}", redisKey, tokenInRedis);
        if (NullUtils.isNull(tokenInRedis)) {
            log.warn("[JWT] Token 校验失败，Redis 未查到 token");
            return null;  // 不再写 Response，直接返回 null
        }
        TokenUser tokenUser = new Gson().fromJson(tokenInRedis, TokenUser.class);
        log.info("[JWT] TokenUser 解析结果: {}", tokenUser);
        String userName = tokenUser.getUsername();

        List<GrantedAuthority> permissionList = new ArrayList<>();
        if (loginProperties.getSaveRoleFlag()) {
            for (String permission : tokenUser.getPermissions()) {
                permissionList.add(new SimpleGrantedAuthority(permission));
            }
        } else {
            permissionList = securityUtil.getCurrUserPerms(userName);
        }

        if (!tokenUser.getSaveLogin()) {
            log.info("[JWT] 非持久化登录，刷新 token 过期时间");
            stringRedisTemplate.opsForValue().set(LoginProperties.USER_TOKEN_PRE + userName, header,
                    loginProperties.getUserTokenInvalidDays(), TimeUnit.MINUTES);
            stringRedisTemplate.opsForValue().set(LoginProperties.HTTP_TOKEN_PRE + header, tokenInRedis,
                    loginProperties.getUserTokenInvalidDays(), TimeUnit.MINUTES);
        }

        if (!NullUtils.isNull(userName)) {
            User user = new User(userName, "", permissionList);
            log.info("[JWT] Authentication successful for user: {}", userName);
            return new UsernamePasswordAuthenticationToken(user, null, permissionList);
        }
        log.warn("[JWT] No username found in token");
        return null;
    }
}
